package com.mune.gulimall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mune.common.enume.OrderStatusEnum;
import com.mune.common.exception.NoStockException;
import com.mune.common.to.SkuHasStockVo;
import com.mune.common.to.mq.StockDetailTo;
import com.mune.common.to.mq.StockLockedTo;
import com.mune.common.to.order.OrderTo;
import com.mune.common.utils.PageUtils;
import com.mune.common.utils.Query;
import com.mune.common.utils.R;
import com.mune.gulimall.ware.dao.WareSkuDao;
import com.mune.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.mune.gulimall.ware.entity.WareOrderTaskEntity;
import com.mune.gulimall.ware.entity.WareSkuEntity;
import com.mune.gulimall.ware.feign.OrderFeignService;
import com.mune.gulimall.ware.feign.ProductFeignService;
import com.mune.gulimall.ware.service.WareOrderTaskDetailService;
import com.mune.gulimall.ware.service.WareOrderTaskService;
import com.mune.gulimall.ware.service.WareSkuService;
import com.mune.gulimall.ware.vo.OrderItemVo;
import com.mune.gulimall.ware.vo.OrderVo;
import com.mune.gulimall.ware.vo.WareSkuLockVo;
import lombok.Data;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {
  @Autowired
  WareSkuDao wareSkuDao;
  @Autowired
  WareOrderTaskService orderTaskService;
  @Autowired
  WareOrderTaskDetailService orderTaskDetailService;
  @Autowired
  ProductFeignService productFeignService;
  @Autowired
  RabbitTemplate rabbitTemplate;
  @Autowired
  OrderFeignService orderFeignService;

  @Override
  public PageUtils queryPage(Map<String, Object> params) {
    QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<>();
    String skuId = (String) params.get("skuId");
    if (!StringUtils.isEmpty(skuId)) {
      wrapper.eq("sku_id", skuId);
    }
    String wareId = (String) params.get("wareId");
    if (!StringUtils.isEmpty(wareId)) {
      wrapper.eq("ware_id", wareId);
    }
    IPage<WareSkuEntity> page = this.page(
            new Query<WareSkuEntity>().getPage(params),
            wrapper
    );

    return new PageUtils(page);
  }

  @Override
  public void addStock(Long skuId, Long wareId, Integer skuNum) {
    WareSkuEntity wareSkuEntity = wareSkuDao.selectOne(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_Id", wareId));
    if (wareSkuEntity == null) {
      WareSkuEntity entity = new WareSkuEntity();
      entity.setSkuId(skuId);
      entity.setStock(skuNum);
      entity.setWareId(wareId);
      entity.setStockLocked(0);
      try {
        R info = productFeignService.info(skuId);
        Map<String, Object> data = (Map<String, Object>) info.get("skuInfo");
        if (info.getCode() == 0) {
          entity.setSkuName((String) data.get("skuName"));
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
      wareSkuDao.insert(entity);
    }
    wareSkuDao.addStock(skuId, wareId, skuNum);

  }

  @Override
  public List<SkuHasStockVo> getSkusHasStock(List<Long> skuIds) {

    List<SkuHasStockVo> collect = skuIds.stream().map(skuId -> {
      SkuHasStockVo skuHasStockVo = new SkuHasStockVo();
      Long count = this.baseMapper.getSkuStock(skuId);
      skuHasStockVo.setSkuId(skuId);
      skuHasStockVo.setHasStock(count != null && count > 0);
      return skuHasStockVo;
    }).collect(Collectors.toList());
    return collect;
  }

  @Transactional(rollbackFor = NoStockException.class)
  @Override
  public Boolean orderLockStock(WareSkuLockVo vo) {
    /**
     * 保存库存工作单详情信息
     *  后续可能释放库存
     */
    WareOrderTaskEntity taskEntity = new WareOrderTaskEntity();
    taskEntity.setOrderSn(vo.getOrderSn());
    taskEntity.setCreateTime(new Date());
    orderTaskService.save(taskEntity);

    List<OrderItemVo> locks = vo.getLocks();
    List<SkuWareHasStock> collect = locks.stream().map(item -> {
      SkuWareHasStock stock = new SkuWareHasStock();
      Long skuId = item.getSkuId();
      stock.setSkuId(skuId);
      stock.setNum(item.getCount());
      // 查询当前商品在哪些仓库有库存
      List<Long> wareIds = wareSkuDao.listWareIdHasSkuStock(skuId);
      stock.setWareId(wareIds);
      return stock;
    }).collect(Collectors.toList());
    for (SkuWareHasStock stock : collect) {
      Boolean skuStocked = false;
      Long skuId = stock.getSkuId();
      List<Long> wareIds = stock.getWareId();
      if (org.springframework.util.StringUtils.isEmpty(wareIds)) throw new NoStockException(skuId);
      for (Long wareId : wareIds) {
        Long count = wareSkuDao.lockSkuStock(skuId, wareId, stock.getNum());
        if (count == 1) {
          /**
           *  锁库存成功、发送延时消息、不受异常回滚
           *  如果每一个商品都锁定成功,将当前商品锁定了几件的工作单记录发给MQ
           *  锁定失败。前面保存的工作单信息都回滚了。发送出去的消息，即使要解锁库存，由于在数据库查不到指定的id，所有就不用解锁
           */

          // 组装信息
          StockDetailTo stockDetailTo = new StockDetailTo(null, skuId, null, stock.getNum(), taskEntity.getId(), wareId, 1);
          StockLockedTo stockLockedTo = new StockLockedTo(taskEntity.getId(), stockDetailTo);
          // 发送消息
          rabbitTemplate.convertAndSend("stock-event-exchange", "stock.locked", stockLockedTo);
          // 封装锁库存详情记录
          WareOrderTaskDetailEntity entity = new WareOrderTaskDetailEntity();
          BeanUtils.copyProperties(stockDetailTo, entity);
          orderTaskDetailService.save(entity);
          skuStocked = true;
          break;
        }
      }
      if (!skuStocked) {
        // 当前商品所有仓库都没锁住
        throw new NoStockException(skuId);
      }
    }

    return true;

  }

  private void unLockStock(Long skuId, Long wareId, Integer num, Long taskDetailId) {
    // 将库存进行解锁
    wareSkuDao.unLockStock(skuId, wareId, num);
    // 更新工作单状态
    WareOrderTaskDetailEntity taskDetailEntity = new WareOrderTaskDetailEntity();
    taskDetailEntity.setId(taskDetailId);
    //变为已解锁
    taskDetailEntity.setLockStatus(2);
    orderTaskDetailService.updateById(taskDetailEntity);
  }

  @Override
  public boolean handleOrderStockRelease(StockLockedTo stockLockedTo) {
    StockDetailTo stockDetail = stockLockedTo.getDetail();
    WareOrderTaskDetailEntity detailEntity = orderTaskDetailService.getById(stockDetail.getId());
    if (detailEntity == null) {
      // 锁库详情纪录被回滚了、说明锁库时发生异常、无需继续解锁、可以批量签收
      return true;
    } else {
      Long taskId = stockLockedTo.getId();
      WareOrderTaskEntity orderTaskInfo = orderTaskService.getById(taskId);
      String orderSn = orderTaskInfo.getOrderSn();
      R r = orderFeignService.getOrderStatus(orderSn);
      if (r.getCode() == 0) {
        OrderVo data = r.getData(new TypeReference<OrderVo>() {
        });
        if (data == null || data.getStatus().equals(OrderStatusEnum.CANCLED.getCode())) {
          if (detailEntity.getLockStatus() == 1) {
            unLockStock(detailEntity.getSkuId(), detailEntity.getWareId(), detailEntity.getSkuNum(), stockDetail.getId());
          }
        }
        return true;
      } else {
        return false;
      }
    }
  }

  @Transactional
  @Override
  public void handleOrderCloseRelease(OrderTo orderTo) {
    String orderSn = orderTo.getOrderSn();
    WareOrderTaskEntity entity = orderTaskService.getOrderTaskByOrderSn(orderSn);
    Long id = entity.getId();
    List<WareOrderTaskDetailEntity> list = orderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>().eq("task_id", id).eq("lock_status", 1));
    for (WareOrderTaskDetailEntity detailEntity : list) {
      unLockStock(detailEntity.getSkuId(), detailEntity.getWareId(), detailEntity.getSkuNum(), detailEntity.getId());
    }
  }

  @Data
  class SkuWareHasStock {
    private Long skuId;
    private Integer num;
    private List<Long> wareId;
  }
}